package com.mic.nio;

import com.mic.nio.pool.NioSelectorRunnablePool;

import java.io.IOException;
import java.nio.channels.Selector;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Created by yjq14 on 2018/2/10.
 */
public abstract class AbstractNioSelector implements Runnable {
    /**
     * 线程池
     */
    private final Executor executor;
    /**
     * 选择器
      */
    protected  Selector selector;
    /**
     * 任务队列
     */
    private final Queue<Runnable> taskQueue = new ConcurrentLinkedDeque<>();
    /**
     * 线程名称
      */
    private String threadName;
    /**
     * 选择器wakeup标记
     */
    protected final AtomicBoolean wakeUp = new AtomicBoolean();

    protected AbstractNioSelector(Executor executor, String threadName, NioSelectorRunnablePool nioSelectorRunnablePool) {
        this.executor = executor;
        this.threadName = threadName;
        this.nioSelectorRunnablePool = nioSelectorRunnablePool;
        openSelector();

    }
    private void openSelector() {
        try {
            this.selector = Selector.open();
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.executor.execute(this);
    }
    protected final void registerTask(Runnable task) {
        taskQueue.add(task);
        Selector selector = this.selector;
        if (selector != null) {
            if (wakeUp.compareAndSet(false, true)) selector.wakeup();
        } else {
            taskQueue.remove(task);
        }

    }
    /**
     * 线程池管理对象
      */
    protected NioSelectorRunnablePool nioSelectorRunnablePool;

    public NioSelectorRunnablePool getNioSelectorRunnablePool() {
        return nioSelectorRunnablePool;
    }

    @Override
    public void run() {
        Thread.currentThread().setName(this.threadName);
        while (true) {
            wakeUp.set(false);
            try {
                select(selector);
                processTaskQueue();
                process(selector);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 处理任务队列
     */
    private void processTaskQueue() {
        for (;;) {
            final Runnable task = taskQueue.poll();
            if (task == null) break;
            task.run();
        }
    }

    /**
     * select 抽象方法
     * @param selector
     * @return
     * @throws IOException
     */
    protected abstract int select(Selector selector) throws IOException;

    /**
     * selector 业务处理抽象
     * @param selector
     * @throws IOException
     */
    protected abstract void process(Selector selector) throws IOException;

}
